use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufReader};
use xml::reader::{EventReader, XmlEvent};
use regex::Regex;

type Attr<'a> = &'a [xml::attribute::OwnedAttribute];

struct MyBatisSQLConverter {
    includes: HashMap<String, String>,
    sql_statements: Vec<String>,
}

#[derive(Debug)]
struct Trim {
    kind: TrimKind,
    prefix: String,
    suffix: String,
    prefix_overrides: Vec<String>,
    suffix_overrides: Vec<String>,
}

#[derive(Debug, PartialEq)]
enum TrimKind {
    Where,
    Set,
    Trim,
}

struct Ctx {
    sql: String,
    trim_stack: Vec<Trim>,
}

impl MyBatisSQLConverter {
    fn new() -> Self {
        Self {
            includes: HashMap::new(),
            sql_statements: Vec::new(),
        }
    }

    /* ---------- 工具 ---------- */
    fn get_attr(attrs: Attr, name: &str) -> Option<String> {
        attrs.iter().find(|a| a.name.local_name == name).map(|a| a.value.clone())
    }

    fn load_includes(&mut self) {
        // 示例：可扩展为从外部文件加载
        self.includes.insert("exampleInclude".to_string(), "column1, column2".to_string());
    }

    fn process_include(&self, refid: &str) -> String {
        self.includes.get(refid).cloned().unwrap_or_default()
    }

    // 把 #{} 和 ${} 统一换成 ?
    fn replace_params(sql: &str) -> String {
        let re = Regex::new(r"\$?\{[^}]+\}").unwrap();
        re.replace_all(sql, "?").to_string()
    }

    /* ---------- 主解析 ---------- */
    fn parse_xml(&mut self, xml_file: &str) -> io::Result<()> {
        let file = File::open(xml_file)?;
        let file = BufReader::new(file);
        let parser = EventReader::new(file);

        let mut ctx = Ctx { sql: String::new(), trim_stack: Vec::new() };
        let mut inside_crud = false;

        for event in parser {
            match event.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))? {
                XmlEvent::StartElement { name, attributes, .. } => {
                    let tag = name.local_name.as_str();
                    match tag {
                        "select" | "insert" | "update" | "delete" => {
                            ctx.sql.clear();
                            inside_crud = true;
                        }
                        "include" => {
                            if let Some(refid) = Self::get_attr(&attributes, "refid") {
                                ctx.sql.push_str(&self.process_include(&refid));
                            }
                        }
                        "where" => {
                            ctx.trim_stack.push(Trim {
                                kind: TrimKind::Where,
                                prefix: "WHERE".to_string(),
                                suffix: "".to_string(),
                                prefix_overrides: vec!["AND ".to_string(), "OR ".to_string()],
                                suffix_overrides: vec![],
                            });
                        }
                        "set" => {
                            ctx.trim_stack.push(Trim {
                                kind: TrimKind::Set,
                                prefix: "SET".to_string(),
                                suffix: "".to_string(),
                                prefix_overrides: vec![],
                                suffix_overrides: vec![",".to_string()],
                            });
                        }
                        "trim" => {
                            ctx.trim_stack.push(Trim {
                                kind: TrimKind::Trim,
                                prefix: Self::get_attr(&attributes, "prefix").unwrap_or_default(),
                                suffix: Self::get_attr(&attributes, "suffix").unwrap_or_default(),
                                prefix_overrides: Self::get_attr(&attributes, "prefixOverrides")
                                    .unwrap_or_default()
                                    .split('|')
                                    .map(str::trim)
                                    .map(str::to_string)
                                    .collect(),
                                suffix_overrides: Self::get_attr(&attributes, "suffixOverrides")
                                    .unwrap_or_default()
                                    .split('|')
                                    .map(str::trim)
                                    .map(str::to_string)
                                    .collect(),
                            });
                        }
                        "foreach" => {
                            // 固定展开 3 份
                            let item = Self::get_attr(&attributes, "item").unwrap_or_default();
                            let open = Self::get_attr(&attributes, "open").unwrap_or_default();
                            let close = Self::get_attr(&attributes, "close").unwrap_or_default();
                            let sep = Self::get_attr(&attributes, "separator").unwrap_or_default();
                            let ph = format!("#{{{}}}", item);
                            let body = vec![ph.as_str(); 3].join(&sep);
                            ctx.sql.push_str(&open);
                            ctx.sql.push_str(&body);
                            ctx.sql.push_str(&close);
                        }
                        // 所有条件标签直接丢弃
                        "if" | "choose" | "when" | "otherwise" => {}
                        _ => {}
                    }
                }

                XmlEvent::Characters(text) => {
                    if inside_crud {
                        let trimmed = text.trim();
                        if !trimmed.is_empty() {
                            ctx.sql.push(' ');
                            ctx.sql.push_str(trimmed);
                        }
                    }
                }

                XmlEvent::EndElement { name } => {
                    let tag = name.local_name.as_str();
                    match tag {
                        "select" | "insert" | "update" | "delete" => {
                            inside_crud = false;
                            while let Some(trim) = ctx.trim_stack.pop() {
                                Self::apply_trim(&mut ctx.sql, &trim);
                            }
                            let sql = Self::replace_params(&ctx.sql);
                            self.sql_statements.push(format!("{};", sql.replace('\n', " ")));
                        }
                        "where" | "set" | "trim" => {
                            if let Some(trim) = ctx.trim_stack.pop() {
                                Self::apply_trim(&mut ctx.sql, &trim);
                            }
                        }
                        _ => {}
                    }
                }

                _ => {}
            }
        }

        Ok(())
    }

    /* ---------- trim 裁剪 ---------- */
    fn apply_trim(sql: &mut String, trim: &Trim) {
        let body = sql.trim().to_string();
        if body.is_empty() {
            return;
        }
        let mut res = String::new();
        if !trim.prefix.is_empty() {
            res.push_str(&trim.prefix);
            res.push(' ');
        }
        let mut tmp = body;
        for po in &trim.prefix_overrides {
            if tmp.starts_with(po) {
                tmp = tmp[po.len()..].to_string();
                break;
            }
        }
        res.push_str(&tmp);
        for so in &trim.suffix_overrides {
            if res.ends_with(so) {
                res.truncate(res.len() - so.len());
                break;
            }
        }
        if !trim.suffix.is_empty() {
            res.push(' ');
            res.push_str(&trim.suffix);
        }
        *sql = res;
    }

    /* ---------- 输出 ---------- */
    fn get_sql_statements(&self) -> &Vec<String> {
        &self.sql_statements
    }
}

/* ---------- main ---------- */
fn main() -> io::Result<()> {
    let mut converter = MyBatisSQLConverter::new();
    converter.load_includes();
    converter.parse_xml("mybatis.xml")?;

    for sql in converter.get_sql_statements() {
        println!("{}", sql);
    }

    Ok(())
}
